package maosy.oauth2.password.service.impl

import maosy.oauth2.password.entities.TbUserEntity
import maosy.oauth2.password.exception.DataNotFoundException
import maosy.oauth2.password.exception.ExistException
import maosy.oauth2.password.exception.ServerException
import maosy.oauth2.password.oto.TBUserOTO
import maosy.oauth2.password.repository.UserRepository
import maosy.oauth2.password.ro.CreateUserRO
import maosy.oauth2.password.ro.UpdateUserRO
import maosy.oauth2.password.service.UserService
import maosy.oauth2.password.util.uuid
import maosy.oauth2.password.vo.TBUserEntityVO
import org.springframework.beans.BeanUtils
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.security.access.prepost.PreAuthorize
import org.springframework.security.core.userdetails.UserDetails
import org.springframework.security.core.userdetails.UserDetailsService
import org.springframework.security.core.userdetails.UsernameNotFoundException
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
import org.springframework.stereotype.Service
import java.util.*
import javax.transaction.Transactional

@Service
@Qualifier("userDetailsService")
open class UserServiceImpl : UserService, UserDetailsService {

    @Autowired
    private lateinit var userRepository: UserRepository

    @Throws(Exception::class)
//    @PreAuthorize("hasAnyAuthority('ADMIN_USER','AGENT_USER','API_CLIENT')")
    override fun getUserByUserName(userName: String): TBUserEntityVO {
        val entity =
            userRepository.queryByUserName(userName) ?: throw DataNotFoundException("$userName user not found.")
        return copy(entity)
    }

    @Throws(Exception::class)
    @Transactional
//    @PreAuthorize("hasAnyAuthority('ADMIN_USER','API_CLIENT')")
    override fun createUser(ro: CreateUserRO): TBUserEntityVO {
        userRepository.queryByUserName(ro.userName)?.let { throw ExistException("${ro.userName} is exist.") }
        val record = TbUserEntity()
        BeanUtils.copyProperties(ro, record)
        record.apply {
            id = String.uuid()
            createTime = Date()
            userState = 1
            isDelete = false
            updateTime = Date()
            password = BCryptPasswordEncoder().encode(ro.password)
        }
        val entity = userRepository.saveAndFlush(record)
        return copy(entity)

    }

    @Throws(Exception::class)
    @Transactional
    override fun updateUser(ro: UpdateUserRO): TbUserEntity {
        val userEntity =
            userRepository.queryByUserName(ro.userName) ?: throw DataNotFoundException("${ro.userName} user not found.")
        return userRepository.save(userEntity.apply {
            ro.email?.let { this.email = it }
            ro.isvId?.let { this.isvId = it }
            ro.remark?.let { this.remark = it }
            ro.telephone?.let { this.telephone = it }
            updateTime = Date()
        })
    }

    @Throws(UsernameNotFoundException::class)
    override fun loadUserByUsername(username: String?): UserDetails {
        username ?: throw ServerException("username is null")
        return TBUserOTO(userRepository.queryByUserName(username))
    }


    private fun copy(entity: TbUserEntity): TBUserEntityVO {
        return TBUserEntityVO().apply {
            BeanUtils.copyProperties(entity, this)
            isDelete = entity.isDelete == true
        }
    }
}